
package com.yuanmei.datas;

import android.util.Log;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import io.realm.Realm;
import io.realm.RealmConfiguration;
import io.realm.RealmResults;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.yuanmei.views.QuickStockApplication;
import com.yuanmei.model.TickQuote;
import com.yuanmei.common.Utils;

/*
https://blog.csdn.net/Llingmiao/article/details/79941066

方法3：http://money.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_MarketData.getKLineData?symbol=[市场][股票代码]&scale=[周期]&ma=no&datalen=[长度]

返回结果：获取5、10、30、60分钟JSON数据；day日期、open开盘价、high最高价、low最低价、close收盘价、volume成交量；向前复权的数据。

注意，最多只能获取最近的1023个数据节点。

例如，http://money.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_MarketData.getKLineData?symbol=sz002095&scale=60&ma=no&datalen=1023，
获取深圳市场002095股票的60分钟数据，获取最近的1023个节点。
* */

public class SinaWebSource {
    private static final String TAG = SinaWebSource.class.getName();

    private final static SimpleDateFormat sSinaDateFormat
            = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
//   /*  set default start date to 1990-01-01: year - 1900, month - 1, day = day - 1 */
//    private final static Date sDefaultStartDate = new Date(90, 0, 0);

    private static String toSinaSymbol(String netEaseCode) {
        String tsCode;
        if (netEaseCode.startsWith("0")) {
            tsCode = "sh" + netEaseCode.substring(1);
        } else {
            tsCode = "sz" + netEaseCode.substring(1);
        }
        return tsCode;
    }

    private static final Map<String, String> sNameMap = new HashMap<>();

    static {
        sNameMap.put("成交时间", "tick");
        sNameMap.put("成交价", "price");
        sNameMap.put("涨跌幅", "pchg");
        sNameMap.put("价格变动", "change");
        sNameMap.put("成交量(手)", "volume");
        sNameMap.put("成交额(元)", "amount");
        sNameMap.put("性质", "type");
    }

    /*
    分时数据
    http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_Transactions.getAllPageTime?date=2019-01-18&symbol=sh603131
    ({detailPages:[{page:1,begin_ts:"14:43:40",end_ts:"15:00:00"},{page:2,begin_ts:"14:27:20",end_ts:"14:43:40"},
    {page:15,begin_ts:"09:25:00",end_ts:"09:41:20"}],detailDate:"2019-01-18"})
    http://vip.stock.finance.sina.com.cn/quotes_service/view/vMS_tradedetail.php?symbol=sh603131&date=2019-01-21&page=10
     * */
    private static List<TickQuote> getTickOfPage(String symbol, String date, int pageNo)
            throws IOException {
        Map<String, String> params = new LinkedHashMap<>();
        params.put("date", date);
        params.put("symbol", symbol);
        params.put("page", String.valueOf(pageNo));
        String reqUrl = "http://vip.stock.finance.sina.com.cn/quotes_service/view/vMS_tradedetail.php"
                + "?" + Utils.buildUrlParameters(params);
        Document doc = Jsoup.connect(reqUrl).get();
        Element table = doc.getElementById("datatbl");
        if (table == null)
            return null;
        List<TickQuote> results = new ArrayList<>();
        Elements htrs = table.select("thead").select("tr");
        Elements hths = htrs.get(0).select("th");
        Map<String, Integer> thMap = new HashMap<>();
        for (int i = 0; i < hths.size(); ++i) {
            Element hth = hths.get(i);
            String normalName = sNameMap.get(hth.text());
            thMap.put(normalName, i);
        }
        Elements htds = htrs.get(0).select("td");
        Map<String, Integer> tdMap = new HashMap<>();
        for (int i = 0; i < htds.size(); ++i) {
            Element htd = htds.get(i);
            String normalName = sNameMap.get(htd.text());
            tdMap.put(normalName, i);
        }
        Elements btrs = table.select("tbody").select("tr");
        for (int i = 0; i < btrs.size(); i++) {
            Elements rths = btrs.get(i).select("th");
            String tick = rths.get(thMap.get("tick")).text();
            String type = rths.get(thMap.get("type")).text();

            Elements rtds = btrs.get(i).select("td");

            float price = Utils.safeToFloat(rtds.get(tdMap.get("price")).text());
            float volume = Utils.safeToFloat(rtds.get(tdMap.get("volume")).text());
            String strAmount = rtds.get(tdMap.get("amount")).text();
            strAmount = strAmount.replaceAll(",", "");
            float amount = Utils.safeToFloat(strAmount);
            if (amount == 0)
                continue;
            results.add(new TickQuote(tick, amount, volume, price, type));
        }

        return results;
    }

    // sina 的历史分时成交明细好像也不太准确
    private static String updateTickDataToRealm(String symbol, String date,
                                              int lrTick, Realm realm) {
        Map<String, String> params = new LinkedHashMap<>();
        params.put("date", date);
        params.put("symbol", symbol);
        String reqUrl = "http://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_Transactions.getAllPageTime"
                + "?" + Utils.buildUrlParameters(params);
        String errorString;
        try {
            Request request = new Request.Builder()
                    .url(reqUrl)
                    .build();
            OkHttpClient client = new OkHttpClient();
    /*
    {detailPages:[{page:1,begin_ts:"14:43:40",end_ts:"15:00:00"},
    {page:15,begin_ts:"09:25:00",end_ts:"09:41:20"}],detailDate:"2019-01-18"})
    */
            Response response = client.newCall(request).execute();
            Log.i(TAG, "jkp Sina tick page ResponseCode("
                    + response.code() +"), URL: " + reqUrl);
            if (response.isSuccessful() && response.body() != null) {
                int update_tick_count = 0;
                String bodyString = response.body().string();
                int leftBraceIndex = bodyString.indexOf('{');
                int rightBraceIndex = bodyString.lastIndexOf('}');
                if (leftBraceIndex < 0 || rightBraceIndex < 0) {
                    errorString = "fail: 新浪源分时数据,无效Body(大括号不匹配)";
                    Log.w(TAG, errorString);
                    return errorString;
                }
                bodyString = bodyString.substring(leftBraceIndex, rightBraceIndex + 1);
//                Log.i(TAG, "bodyString 222 =:\n " + bodyString);
                JSONObject resultObj = new JSONObject(bodyString);
                JSONArray pagesArray = resultObj.getJSONArray("detailPages");
                QuickStockApplication.showProgressBar();
                int page_count = pagesArray.length();
                for (int i = 0; i < page_count; i++) {
                    JSONObject pageInfo = pagesArray.getJSONObject(i);
                    int pageNo = pageInfo.getInt("page");
                    int end_tick = Utils.parseTickFromString(pageInfo.getString("end_ts"));

                    int progress = (i + 1) * 100 / page_count;
//                    Log.i(TAG, "JKPD notify progress=" + progress
//                            + ", i=" + i + ", pageNo=" + pageNo
//                            + ", page_count=" + page_count
//                            + ", begin_ts=" + pageInfo.getString("begin_ts")
//                            + ", end_ts=" + pageInfo.getString("end_ts")
//                           );
                    QuickStockApplication.notifyProgress(progress);
                    if (lrTick > end_tick) {
                        // sina page 是倒序，最后的在最前面
                        break;
                    }
                    List<TickQuote> pageTickQuotes = getTickOfPage(symbol, date, pageNo);
                    if (pageTickQuotes != null) {
                        realm.beginTransaction();
                        for (TickQuote quote : pageTickQuotes) {
                            realm.insertOrUpdate(quote);
                        }
                        realm.commitTransaction();
                        update_tick_count += pageTickQuotes.size();
                    }
                }
                QuickStockApplication.hideProgressBar();
                errorString = "ok: 新浪源更新分时数据("
                        + update_tick_count + "条)";
            } else {
                errorString = "fail :新浪源分时数据 getAllPageTime Fail: response code=" + response.code() + "\n"
                        + " Response=" + response.toString();
                Log.e(TAG, errorString);
            }
            return errorString;
        } catch (JSONException e) {
            errorString = "fail : 新浪源分时数据 JSONException:";
            Log.e(TAG, errorString, e);
        } catch (IOException e) {
            errorString = "fail : 新浪源分时数据 IOException:";
            Log.e(TAG, errorString, e);
        }
        return errorString;
    }

    private static void updateRealTimeTickRealm(String netEaseCode, Date tickDate) {
        String symbol = toSinaSymbol(netEaseCode);
        String date = sSinaDateFormat.format(tickDate);
        RealmConfiguration tickRealmConfig = new RealmConfiguration.Builder()
//                .name(Utils.getRealTimeTickRealmFile(netEaseCode, tickDate))
                .name(Utils.getTickRealmFile(netEaseCode, tickDate))
                .deleteRealmIfMigrationNeeded()
                .build();
        Realm realm = Realm.getInstance(tickRealmConfig);
        RealmResults<TickQuote> results = realm.where(TickQuote.class).findAll();
        int lastRecordTick = 0;
        if (results.size() > 0) {
            Number maxTick = results.max("tick");
            if (maxTick != null)
                lastRecordTick = maxTick.intValue();
        }
        String errorString =
                updateTickDataToRealm(symbol, date, lastRecordTick, realm);
        realm.close();
        if(!Realm.compactRealm(tickRealmConfig)) {
            Log.w(TAG, "After use SINA_HTML update " + netEaseCode + " today tick quotes, compact realm fail");
        }
        if (errorString.startsWith("ok")) {
            QuickStockApplication.notifyToast("获取实时分时数据成功: " + errorString);
        } else {
            QuickStockApplication.notifyToast("获取实时分时数据失败: " + errorString);

        }
     }

    public static List<TickQuote> getRealTimeTickData(String netEaseCode, Date tickDate) {
        updateRealTimeTickRealm(netEaseCode, tickDate);
        return Utils.getTickQuotesFromRealm(
//                Utils.getRealTimeTickRealmFile(netEaseCode, tickDate));
                Utils.getTickRealmFile(netEaseCode, tickDate));
    }

    public static String downloadHistoryTickData(
            String netEaseCode, Date tickDate) {
        String errorString = "ok";
        String symbol = toSinaSymbol(netEaseCode);
        String date = sSinaDateFormat.format(tickDate);
        RealmConfiguration tickRealmConfig = new RealmConfiguration.Builder()
                .name(Utils.getTickRealmFile(netEaseCode, tickDate))
                .deleteRealmIfMigrationNeeded()
                .build();
        Realm realm = Realm.getInstance(tickRealmConfig);
        RealmResults<TickQuote> results = realm.where(TickQuote.class).findAll();
        int lastRecordTick = 0;
        if (results.size() > 0) {
            Number maxTick = results.max("tick");
            if (maxTick != null) {
                lastRecordTick = maxTick.intValue();
            }
        }
        if (lastRecordTick < 150000) {
            errorString = updateTickDataToRealm(symbol, date, lastRecordTick, realm);
        }
        realm.close();
        if(!Realm.compactRealm(tickRealmConfig)) {
            Log.w(TAG, "After use SINA_HTML update " + netEaseCode + " today tick quotes, compact realm fail");
        }
        return errorString;
    }

}
